package com.kk.nio.work01.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class FileClientHandle implements Runnable {

    /**
     * 服务器的地址
     */
    private String host;

    /**
     * 端口
     */
    private int port;

    /**
     * 多路复用选择器对象
     */
    private Selector selector;

    /**
     * socket通道
     */
    private SocketChannel channelCli;

    /**
     * 当前的是否停止,默认false，开始状态
     */
    private volatile boolean stop = false;

    public FileClientHandle(String host, int port) {
        this.host = host == null ? "localhost" : host;
        this.port = port;

        try {
            selector = Selector.open();
            // 打开客户端流通道
            channelCli = SocketChannel.open();
            // 设置为非阻塞模式
            channelCli.configureBlocking(false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        // 进行与服务器的发送对话
        try {
            this.doConnection();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 进行消息的接收
        while (!stop) {
            try {
                // 设置1秒轮循一次
                selector.select(1000);
                // 检查是否有数据接入
                Set<SelectionKey> setKey = selector.selectedKeys();
                // 进行遍历
                Iterator<SelectionKey> iterKey = setKey.iterator();

                while (iterKey.hasNext()) {
                    SelectionKey itemKey = iterKey.next();

                    try {
                        this.receive(itemKey);
                    } catch (Exception e) {
                        e.printStackTrace();
                        itemKey.cancel();
                        if (itemKey.channel() != null) {
                            itemKey.channel().close();
                        }
                    }

                    iterKey.remove();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void receive(SelectionKey itemKey) throws IOException {
        // 如果当前键是有效的
        if (itemKey.isValid()) {

            // 如果当前的键为连接
            if (itemKey.isConnectable()) {

                // 得到服务器端的通道
                SocketChannel clientChannel = (SocketChannel) itemKey.channel();

                // 如果已经完成连接
                if (clientChannel.finishConnect()) {
                    clientChannel.register(selector, SelectionKey.OP_READ);
                    // 在完成连接后发送一条消息
                    doSend(clientChannel);
                }
            }
            // 如果当前通道为读取操作
            else if (itemKey.isReadable()) {

                // 得到服务器端的通道
                SocketChannel clientChannel = (SocketChannel) itemKey.channel();

                ByteBuffer buf = ByteBuffer.allocate(512);

                while (clientChannel.read(buf) > 0) {
                    buf.flip();

                    String body = new String(buf.array());

                    System.out.println("收到服务器响应:" + body);

                    buf.clear();
                }

            }
        }
    }

    /**
     * 进行连接
     * 
     * @throws IOException
     */
    private void doConnection() throws IOException {
        // 如果服用服务器的连接已经连接成功，需要将读取消息注册到多路复用器上，然后发送消息
        if (channelCli.connect(new InetSocketAddress(host, port))) {
            // 注册读取操作到多路复用器上
            channelCli.register(selector, SelectionKey.OP_READ);
        } else {
            // 注册连接
            channelCli.register(selector, SelectionKey.OP_CONNECT);
        }
    }

    /**
     * 发送消息
    * 方法描述
    * @param sc
    * @throws IOException
    * @创建日期 2016年11月4日
    */
    private void doSend(SocketChannel sc) throws IOException {
        byte[] sendbyt = "client send request to server".getBytes();

        ByteBuffer buff = ByteBuffer.allocate(512);

        buff.put(sendbyt);

        buff.flip();

        sc.write(buff);

        System.out.println("消息发送完毕");
    }

}
